[XEN] Fix domctl for changing VCPU affinity.
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Thu, 21 Sep 2006 18:34:00 +0000 (19:34 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Thu, 21 Sep 2006 18:34:00 +0000 (19:34 +0100)
Now works for any VCPU, including the caller's VCPU.

By not synchronously pausing the affected VCPU we avoid
any risk of deadlock.

Signed-off-by: Keir Fraser <keir@xensource.com>
xen/common/domctl.c
xen/common/sched_credit.c
xen/common/sched_sedf.c
xen/common/schedule.c
xen/include/xen/sched-if.h
xen/include/xen/sched.h

index a053145a1a2a63dd6bc24904f7157d9318e383f5..ecb77cd85dbc63404c7234ce56ef33d82e1f2ee9 100644 (file)
@@ -356,37 +356,20 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domctl_t) u_domctl)
         struct vcpu *v;
         cpumask_t new_affinity;
 
+        ret = -ESRCH;
         if ( d == NULL )
-        {
-            ret = -ESRCH;            
             break;
-        }
-        
-        if ( (op->u.vcpuaffinity.vcpu >= MAX_VIRT_CPUS) ||
-             !d->vcpu[op->u.vcpuaffinity.vcpu] )
-        {
-            ret = -EINVAL;
-            put_domain(d);
-            break;
-        }
 
-        v = d->vcpu[op->u.vcpuaffinity.vcpu];
-        if ( v == NULL )
-        {
-            ret = -ESRCH;
-            put_domain(d);
-            break;
-        }
+        ret = -EINVAL;
+        if ( op->u.vcpuaffinity.vcpu >= MAX_VIRT_CPUS )
+            goto vcpuaffinity_out;
+
+        ret = -ESRCH;
+        if ( (v = d->vcpu[op->u.vcpuaffinity.vcpu]) == NULL )
+            goto vcpuaffinity_out;
 
         if ( op->cmd == XEN_DOMCTL_setvcpuaffinity )
         {
-            if ( v == current )
-            {
-                ret = -EINVAL;
-                put_domain(d);
-                break;
-            }
-
             xenctl_cpumap_to_cpumask(
                 &new_affinity, &op->u.vcpuaffinity.cpumap);
             ret = vcpu_set_affinity(v, &new_affinity);
@@ -395,8 +378,10 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domctl_t) u_domctl)
         {
             cpumask_to_xenctl_cpumap(
                 &op->u.vcpuaffinity.cpumap, &v->cpu_affinity);
+            ret = 0;
         }
 
+    vcpuaffinity_out:
         put_domain(d);
     }
     break;
index 5e02ead91f29bcb5aac1b31dcb1bf70cd0b42d29..1dd54bbf52e40f66a58bebd9693fb076f736f54c 100644 (file)
@@ -571,47 +571,6 @@ csched_vcpu_wake(struct vcpu *vc)
     __runq_tickle(cpu, svc);
 }
 
-static int
-csched_vcpu_set_affinity(struct vcpu *vc, cpumask_t *affinity)
-{
-    unsigned long flags;
-    int lcpu;
-
-    if ( vc == current )
-    {
-        /* No locking needed but also can't move on the spot... */
-        if ( !cpu_isset(vc->processor, *affinity) )
-            return -EBUSY;
-
-        vc->cpu_affinity = *affinity;
-    }
-    else
-    {
-        /* Pause, modify, and unpause. */
-        vcpu_pause(vc);
-
-        vc->cpu_affinity = *affinity;
-        if ( !cpu_isset(vc->processor, vc->cpu_affinity) )
-        {
-            /*
-             * We must grab the scheduler lock for the CPU currently owning
-             * this VCPU before changing its ownership.
-             */
-            vcpu_schedule_lock_irqsave(vc, flags);
-            lcpu = vc->processor;
-
-            vc->processor = first_cpu(vc->cpu_affinity);
-
-            spin_unlock_irqrestore(&per_cpu(schedule_data, lcpu).schedule_lock,
-                                   flags);
-        }
-
-        vcpu_unpause(vc);
-    }
-
-    return 0;
-}
-
 static int
 csched_dom_cntl(
     struct domain *d,
@@ -1227,8 +1186,6 @@ struct scheduler sched_credit_def = {
     .sleep          = csched_vcpu_sleep,
     .wake           = csched_vcpu_wake,
 
-    .set_affinity   = csched_vcpu_set_affinity,
-
     .adjust         = csched_dom_cntl,
 
     .tick           = csched_tick,
index 8559e3bdf513b75877a88fb67069684ae12e1ed8..02c884d06e66df71854d8eb19140af473762611f 100644 (file)
@@ -1175,20 +1175,6 @@ void sedf_wake(struct vcpu *d)
 }
 
 
-static int sedf_set_affinity(struct vcpu *v, cpumask_t *affinity)
-{
-    if ( v == current )
-        return cpu_isset(v->processor, *affinity) ? 0 : -EBUSY;
-
-    vcpu_pause(v);
-    v->cpu_affinity = *affinity;
-    v->processor = first_cpu(v->cpu_affinity);
-    vcpu_unpause(v);
-
-    return 0;
-}
-
-
 /* Print a lot of useful information about a domains in the system */
 static void sedf_dump_domain(struct vcpu *d)
 {
@@ -1449,7 +1435,6 @@ struct scheduler sched_sedf_def = {
     .sleep          = sedf_sleep,
     .wake           = sedf_wake,
     .adjust         = sedf_adjust,
-    .set_affinity   = sedf_set_affinity
 };
 
 /*
index a31bdb369c687a1730166760fc521676fe58d06b..61f8f4c3debf3bb16695bf4f5e9118f7dd63686c 100644 (file)
@@ -181,15 +181,56 @@ void vcpu_wake(struct vcpu *v)
     TRACE_2D(TRC_SCHED_WAKE, v->domain->domain_id, v->vcpu_id);
 }
 
+static void vcpu_migrate(struct vcpu *v)
+{
+    cpumask_t online_affinity;
+    unsigned long flags;
+    int old_cpu;
+
+    vcpu_schedule_lock_irqsave(v, flags);
+
+    if ( test_bit(_VCPUF_running, &v->vcpu_flags) ||
+         !test_and_clear_bit(_VCPUF_migrating, &v->vcpu_flags) )
+    {
+        vcpu_schedule_unlock_irqrestore(v, flags);
+        return;
+    }
+
+    /* Switch to new CPU, then unlock old CPU. */
+    old_cpu = v->processor;
+    cpus_and(online_affinity, v->cpu_affinity, cpu_online_map);
+    v->processor = first_cpu(online_affinity);
+    spin_unlock_irqrestore(
+        &per_cpu(schedule_data, old_cpu).schedule_lock, flags);
+
+    /* Wake on new CPU. */
+    vcpu_wake(v);
+}
+
 int vcpu_set_affinity(struct vcpu *v, cpumask_t *affinity)
 {
     cpumask_t online_affinity;
+    unsigned long flags;
 
     cpus_and(online_affinity, *affinity, cpu_online_map);
     if ( cpus_empty(online_affinity) )
         return -EINVAL;
 
-    return SCHED_OP(set_affinity, v, affinity);
+    vcpu_schedule_lock_irqsave(v, flags);
+
+    v->cpu_affinity = *affinity;
+    if ( !cpu_isset(v->processor, v->cpu_affinity) )
+        set_bit(_VCPUF_migrating, &v->vcpu_flags);
+
+    vcpu_schedule_unlock_irqrestore(v, flags);
+
+    if ( test_bit(_VCPUF_migrating, &v->vcpu_flags) )
+    {
+        vcpu_sleep_nosync(v);
+        vcpu_migrate(v);
+    }
+
+    return 0;
 }
 
 /* Block the currently-executing domain until a pertinent event occurs. */
@@ -555,6 +596,13 @@ static void __enter_scheduler(void)
     context_switch(prev, next);
 }
 
+void context_saved(struct vcpu *prev)
+{
+    clear_bit(_VCPUF_running, &prev->vcpu_flags);
+
+    if ( unlikely(test_bit(_VCPUF_migrating, &prev->vcpu_flags)) )
+        vcpu_migrate(prev);
+}
 
 /****************************************************************************
  * Timers: the scheduler utilises a number of timers
index 4cfed1cbb84935a29da83b748c304ba5616ff8ab..dc3b04fa22e090ef3078c762039a8e9006127526 100644 (file)
@@ -69,8 +69,6 @@ struct scheduler {
     void         (*sleep)          (struct vcpu *);
     void         (*wake)           (struct vcpu *);
 
-    int          (*set_affinity)   (struct vcpu *, cpumask_t *);
-
     struct task_slice (*do_schedule) (s_time_t);
 
     int          (*adjust)         (struct domain *,
index fb98daeecc6fb88ff9f52674bf073e193748df66..7e435bf2cfc37b3a10c80bcfa5af0e0c63b35010 100644 (file)
@@ -312,7 +312,7 @@ extern void context_switch(
  * saved to memory. Alternatively, if implementing lazy context switching,
  * ensure that invoking sync_vcpu_execstate() will switch and commit @prev.
  */
-#define context_saved(prev) (clear_bit(_VCPUF_running, &(prev)->vcpu_flags))
+extern void context_saved(struct vcpu *prev);
 
 /* Called by the scheduler to continue running the current VCPU. */
 extern void continue_running(
@@ -386,9 +386,12 @@ extern struct domain *domain_list;
  /* VCPU is paused by the hypervisor? */
 #define _VCPUF_paused          11
 #define VCPUF_paused           (1UL<<_VCPUF_paused)
-/* VCPU is blocked awaiting an event to be consumed by Xen. */
+ /* VCPU is blocked awaiting an event to be consumed by Xen. */
 #define _VCPUF_blocked_in_xen  12
 #define VCPUF_blocked_in_xen   (1UL<<_VCPUF_blocked_in_xen)
+ /* VCPU affinity has changed: migrating to a new CPU. */
+#define _VCPUF_migrating       13
+#define VCPUF_migrating        (1UL<<_VCPUF_migrating)
 
 /*
  * Per-domain flags (domain_flags).
@@ -418,9 +421,15 @@ extern struct domain *domain_list;
 static inline int vcpu_runnable(struct vcpu *v)
 {
     return ( !(v->vcpu_flags &
-               (VCPUF_blocked|VCPUF_down|VCPUF_paused|VCPUF_blocked_in_xen)) &&
+               ( VCPUF_blocked |
+                 VCPUF_down |
+                 VCPUF_paused |
+                 VCPUF_blocked_in_xen |
+                 VCPUF_migrating )) &&
              !(v->domain->domain_flags &
-               (DOMF_shutdown|DOMF_ctrl_pause|DOMF_paused)) );
+               ( DOMF_shutdown |
+                 DOMF_ctrl_pause |
+                 DOMF_paused )));
 }
 
 void vcpu_pause(struct vcpu *v);